package org.example.order;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.util.Matrix;
import org.krysalis.barcode4j.HumanReadablePlacement;
import org.krysalis.barcode4j.impl.code128.Code128Bean;
import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 *  最终 ，然后具体拆分一个个方法
 */

public class CreatePdfUtil {

    private static final float PAGE_WIDTH = 131 * 2.83465f;
    private static final float PAGE_HEIGHT = 72 * 2.83465f;

    private static final float MARGIN = 10;


    public static void main(String[] args) {
        try {
            PrintOrder printOrder = new PrintOrder();
            printOrder.setId(204433307096715264L);
            printOrder.setOrderId(204130596568567808L);
            printOrder.setWayBillNo("17LE-WL-020113264676");
            printOrder.setSelfPickUpName("阳光超市西湖店大门口");
            printOrder.setSelfPickUpNo("0003");
            printOrder.setUserName("王*");
            printOrder.setUserPhone("138****1234");
            printOrder.setUserAddress("山西省运城市盐湖区河东东街333号");

            List<String> orderProductNames =new ArrayList<>();
            orderProductNames.add("迷你折叠露营车 *1");
            orderProductNames.add("iphone16 银白色 256G proMax *15");
            orderProductNames.add("金丝楠木床 1.8*2 *2");
            orderProductNames.add("小米全智能空调 省电 高效 好 5匹 一级能耗 *2");
            orderProductNames.add("蓝色妖姬 收藏款 蓝色 20支 中支烟 整条*6");
            orderProductNames.add("minibulu *1");

            printOrder.setOrderDetailList(orderProductNames);

            printOrder.setOrderProductCount(16);
            savePdf(printOrder);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static byte[] savePdf(PrintOrder printOrder) throws IOException {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        // 创建PDF文档
        PDDocument document = new PDDocument();

        // 创建页面，设置纸张大小为131mm x 72mm
        PDPage page = new PDPage(new PDRectangle(203.27f,371.63f)); // 131mm * 72mm 转为pt
        document.addPage(page);

        // 加载中文字体（你可以选择其他支持中文的字体文件）
        PDType0Font font = PDType0Font.load(document, new File("C:\\Windows\\Fonts\\simhei.ttf"));

        // 创建内容流
        PDPageContentStream contentStream = new PDPageContentStream(document, page);

        //顶部
        writeText(contentStream,font,6,"客服热线  8355558",10,360);
        // 写入时间
        String checkTime = "已验视    "+ DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        writeText(contentStream,font,6,checkTime,10,350);

        //顶部图片
        try {
            PDImageXObject logo = PDImageXObject.createFromFile("C:\\Users\\15711\\Desktop\\微信图片_20250509194925.jpg", document);
            float logoWidth = 30;
            float logoHeight = logo.getHeight() * logoWidth / logo.getWidth();
            //contentStream.drawImage(logo, 170, 340, logoWidth, logoHeight);
            writeImage(contentStream,logo,170,340,logoWidth,logoHeight);
        } catch (IOException e) {
            System.err.println("无法加载logo: " + e.getMessage());
        }
        //条形码
        // 运单号
        byte[] barcodeImageData = generateBarcodeImage(printOrder.getWayBillNo());
        // 从字节数组创建PDF图像对象
        PDImageXObject pdImage = PDImageXObject.createFromByteArray(
                document, barcodeImageData, "barcode");
        contentStream.drawImage(pdImage, 10, 295, 180, 35);

        //运单号
        // 运单号
        String orderNo = "运单号      "+printOrder.getWayBillNo();
        writeText(contentStream,font, 9,orderNo,10,285);

        //整体边框
        contentStream.setLineWidth(1f);
        contentStream.moveTo(5, 30);
        contentStream.lineTo(195, 30);  // 顶部边框
        contentStream.lineTo(195, 280);  // 右侧边框
        contentStream.lineTo(5, 280);   // 底部边框
        contentStream.lineTo(5, 30);   // 左侧边框
        contentStream.stroke();

        //自提点
        writeText(contentStream,font,10,"自提点  ",11,255);

        //  多行文字换行
        /*writeText(contentStream,font,6,"西建天茂城东二门 京东超市",45,262);
        writeText(contentStream,font,6,"大门口",45,255);*/
        writeTextLinebreak(contentStream,font,8,printOrder.getSelfPickUpName(),45,262,10,7);
        //自提点下的行线
        contentStream.setLineWidth(0.5f);
        contentStream.moveTo(5, 240);
        contentStream.lineTo(195, 240);  // 条形码从位置130到240
        contentStream.stroke();

        //自提点条形码
        //自提点编码
        String selfPickUpNo = printOrder.getSelfPickUpNo();
        String selfPickUpNoBarcode = "0359AF-"+selfPickUpNo;
        writeText(contentStream,font, 8,selfPickUpNoBarcode,145,265);
        //自提点条形码
        byte[] barcodeImageData2 = generateBarcodeImage(selfPickUpNoBarcode);
        // 从字节数组创建PDF图像对象
        PDImageXObject pdImage2 = PDImageXObject.createFromByteArray(
                document, barcodeImageData2, "barcode");
        contentStream.drawImage(pdImage2, 145, 245, 45, 15);


        //收件人信息
        writeText(contentStream,font,12,"收",15,220);

        // 收件人，手机
        writeText(contentStream,font, 7,printOrder.getUserName(),45,225);
        writeText(contentStream,font, 7,printOrder.getUserPhone(),45,215);

        // 收件人地址 换行
        /*writeText(contentStream,font, 6,"山西省运城市席间天猫他",15,200);
        writeText(contentStream,font, 10,"**************",15,190);*/
        writeTextLinebreak(contentStream,font,6,printOrder.getUserAddress(),15,205,15,10);
        //二维码
        contentStream.setLineWidth(0.5f);
        contentStream.moveTo(145, 190);
        contentStream.lineTo(195, 190);  // 顶部边框
        contentStream.lineTo(195, 240);  // 右侧边框
        contentStream.lineTo(145, 240);   // 底部边框
        contentStream.lineTo(145, 190);   // 左侧边框
        contentStream.stroke();

        try {
            PDImageXObject logo = PDImageXObject.createFromFile("C:\\Users\\15711\\Desktop\\www.17.life.png", document);
            contentStream.drawImage(logo, 146, 190, 48, 48);
        } catch (IOException e) {
            System.err.println("无法加载logo: " + e.getMessage());
        }

        //收件人下的行线
        contentStream.setLineWidth(0.5f);
        contentStream.moveTo(5, 190);
        contentStream.lineTo(195, 190);  // 条形码从位置130到240
        contentStream.stroke();

        //寄件人信息
        //寄件人
        writeText(contentStream,font, 8,"寄",10,175);
        writeText(contentStream,font, 7,"17乐生活  0359-8755558   山西省运城市涑水",40,180);
        writeText(contentStream,font, 7,"东街041号",40,170);

        //订单信息
        //list   换行
        //writeText(contentStream,font,10,"苹果 3斤*3",17,150);
        writeTextList(contentStream,font,8,17,140,printOrder.getOrderDetailList(),11);

        //底部左侧内容
        writeText(contentStream,font,5,"惊喜商场 每日惊喜不断",7,22);

        //底部右侧内容
        writeText(contentStream,font,5,"已验视",170,20);

        drawCircle(contentStream, 170, 50, 17);

        //圆圈内容
        //底部右侧内容
        // 数字动态
        Integer orderProductCount = printOrder.getOrderProductCount();
        String orderProductCountStr = orderProductCount+"件";
        writeText(contentStream,font,12,orderProductCountStr,160,45);

        contentStream.close();

        // 保存PDF文件
        //document.save("C:\\Users\\15711\\Desktop\\shipping_label2.pdf");
        document.save(outputStream);
        // 关闭文档
        document.close();

        System.out.println("PDF文件已生成！");

        return outputStream.toByteArray();
    }

    //写入单个问题
    public static void writeText(PDPageContentStream contentStream, PDType0Font font, int fontSize, String text, float x, float y) throws IOException {
        //开始写入文本
        contentStream.beginText();
        //设置字体和字号
        contentStream.setFont(font, fontSize);
        //设置文本位置
        Matrix matrix = new Matrix(1, 0, 0, 1, x, y);
        contentStream.setTextMatrix(matrix);
        //文本内容
        contentStream.showText(text);
        // 结束内容流
        contentStream.endText();
    }

    //文本过长换行
    //返回使用了多少行高
    public static Integer writeTextLinebreak(PDPageContentStream contentStream, PDType0Font font, int fontSize, String text, float x, float y,Integer length,Integer lineHigh) throws IOException {
        if(StrUtil.isNotBlank(text)){
            int textLength = text.length();

            if(textLength<length){
                writeText(contentStream,font,fontSize,text,x,y);
                return lineHigh;
            }else{
                // 计算需要多少行
                int hang = textLength%length == 0 ? textLength/length : textLength/length+1;
                for (int i = 0; i < hang; i++) {
                    int start = i*length;
                    int end = start+length;
                    if(end>textLength){
                        end = textLength;
                    }
                    writeText(contentStream,font,fontSize,text.substring(start,end),x,y-lineHigh*i);
                }
                return lineHigh*hang;
            }
        }
        return 0;
    }


    public static void writeTextList(PDPageContentStream contentStream, PDType0Font font, int fontSize, float x, float y,List<String> textList,Integer lineHigh) throws IOException {
        if(CollectionUtil.isNotEmpty(textList)){
            float realY = y;
            Integer realUseY = 0;
            for (int i = 0; i < textList.size(); i++) {
                String text = textList.get(i);
                //writeText(contentStream,font,fontSize,textList.get(i),x,y-lineHigh*i);
                 realY = realY-realUseY;
                 realUseY = writeTextLinebreak(contentStream, font, fontSize, text, x, realY, 25, lineHigh);
            }
        }

    }


    public static void writeImage(PDPageContentStream contentStream, PDImageXObject pdImage, float x, float y, float width, float height) throws IOException {
        contentStream.drawImage(pdImage, x, y, width, height);
    }

    /**
     * 绘制圆形轮廓
     *
     * @param contentStream PDF内容流
     * @param centerX 圆心X坐标
     * @param centerY 圆心Y坐标
     * @param radius 圆半径
     */
    public static void drawCircle(PDPageContentStream contentStream,
                                  float centerX, float centerY,
                                  float radius) throws IOException {
        // 设置线条属性
        contentStream.setLineWidth(0.5f); // 边框宽度

        // 使用贝塞尔曲线绘制圆形
        float k = 0.552284749831f; // 贝塞尔曲线控制点常数

        // 移动到起始点 (右侧点)
        contentStream.moveTo(centerX + radius, centerY);

        // 绘制四个贝塞尔曲线段 (每个象限一个)

        // 右下象限
        contentStream.curveTo(
                centerX + radius, centerY + radius * k,  // 第一个控制点
                centerX + radius * k, centerY + radius,  // 第二个控制点
                centerX, centerY + radius                // 终点 (底部点)
        );

        // 左下象限
        contentStream.curveTo(
                centerX - radius * k, centerY + radius,   // 第一个控制点
                centerX - radius, centerY + radius * k,   // 第二个控制点
                centerX - radius, centerY                 // 终点 (左侧点)
        );

        // 左上象限
        contentStream.curveTo(
                centerX - radius, centerY - radius * k,   // 第一个控制点
                centerX - radius * k, centerY - radius,   // 第二个控制点
                centerX, centerY - radius                 // 终点 (顶部点)
        );

        // 右上象限
        contentStream.curveTo(
                centerX + radius * k, centerY - radius,   // 第一个控制点
                centerX + radius, centerY - radius * k,   // 第二个控制点
                centerX + radius, centerY                 // 终点 (右侧点)
        );

        // 关闭路径并描边
        contentStream.closePath();
        contentStream.stroke();
    }



    /**
     * 直接在内存中生成条形码图像数据
     *
     * @param content 条形码内容
     * @return 包含PNG图像数据的字节数组
     */
    private static byte[] generateBarcodeImage(String content) throws IOException {
        // 获取条形码生成器
        //BarcodeUtil util = BarcodeUtil.getInstance();
        Code128Bean bean = new Code128Bean();

        // 设置条宽度 (mm)
        bean.setModuleWidth(0.20);

// 设置条高度 (mm)
        bean.setBarHeight(10.0);

// 设置空白区大小
        bean.setQuietZone(5);

        //设置是否显示条形码内容
        bean.doQuietZone(false);

        Boolean showText = false;

        if (showText) {
            bean.setMsgPosition(HumanReadablePlacement.HRP_BOTTOM);
            bean.setFontSize(2.0);
        } else {
            bean.setMsgPosition(HumanReadablePlacement.HRP_NONE);
        }

        int dpi = 150;
        int imageType = BufferedImage.TYPE_BYTE_BINARY;

        // 创建内存输出流
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 创建画布提供程序
        BitmapCanvasProvider canvas = new BitmapCanvasProvider(
                out, "image/png", dpi, imageType, false, 0);

        // 生成条形码
        bean.generateBarcode(canvas, content);
        canvas.finish();

        return out.toByteArray();
    }


    /**
     * 直接在内存中生成条形码图像数据
     *
     * @param content 条形码内容
     * @return 包含PNG图像数据的字节数组
     */
    private static byte[] generateBarcodeImage2(String content) throws IOException {
        // 获取条形码生成器
        //BarcodeUtil util = BarcodeUtil.getInstance();
        Code128Bean bean = new Code128Bean();

        // 设置条宽度 (mm)
        bean.setModuleWidth(0.20);

// 设置条高度 (mm)
        bean.setBarHeight(5.0);

// 设置空白区大小
        bean.setQuietZone(2);

        //设置是否显示条形码内容
        bean.doQuietZone(false);

        Boolean showText = false;

        if (showText) {
            bean.setMsgPosition(HumanReadablePlacement.HRP_BOTTOM);
            bean.setFontSize(2.0);
        } else {
            bean.setMsgPosition(HumanReadablePlacement.HRP_NONE);
        }

        int dpi = 150;
        int imageType = BufferedImage.TYPE_BYTE_BINARY;

        // 创建内存输出流
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 创建画布提供程序
        BitmapCanvasProvider canvas = new BitmapCanvasProvider(
                out, "image/png", dpi, imageType, false, 0);

        // 生成条形码
        bean.generateBarcode(canvas, content);
        canvas.finish();

        return out.toByteArray();
    }

}
